﻿#include "businessinit.h"

#include <AppConfig/appconfig.h>
#include "businessnetwork.h"
#include <fstream>
#include <iostream>
#include <CipherUtils/cipherutils.h>
#include "businessmsghandler.h"
#include "businesssql.h"
#include <FileManager/filemanager.h>
#include <ThreadPool/threadpool.h>
#include <Debug/DEBUG.hpp>

// 初始化所有的模块
bool BusinessInit::InitModules() {

    // 判断当前文件夹下是不是有全局配置文件，如果没有，则创建一个
    std::string configFilePath = "./CONFIG";
    if (!std::filesystem::exists(configFilePath)) {
        std::ofstream ofs(configFilePath);
        if (!ofs) {
            throw std::runtime_error("创建全局文件失败：" + configFilePath);
            return false;
        }
        ofs.close();
    }

    // check file dir

    BusinessNetWork::getInstance().initNet();

    BusinessMsgHandler::getInstance().init();

    FileManager& fileManager = FileManager::getInstance();
    fileManager.init();

    ThreadPool::getInstance().commit([&](){fileManager.processQueue();});

    return true;
}

void BusinessInit::InitProgram() {
    CipherUtils& utils = CipherUtils::getInstance();
    AppConfig& config = AppConfig::getInstance();
    ///
    /// \brief AESIV
    /// get AESKey and AESIV, if exists, generate a new one

    std::string AESIV;
    std::cout << "please input programKey, if empty, it will generate a new one";
    getline(std::cin, AESIV);
    if (AESIV.empty()) {
        auto AES_IV = utils.AES_generate();
        std::shared_ptr<unsigned char[]> AES_IV_BINTemp(new unsigned char[256/8 + 128/8]);
        memcpy(AES_IV_BINTemp.get(), AES_IV[0].get(), 256/8);
        memcpy(AES_IV_BINTemp.get() + 256/8, AES_IV[1].get(), 128/8);
        std::string AES_IV_BASE64 = utils.Base64_Encode(AES_IV_BINTemp.get(), 256/8 + 128/8);
        std::cout << "please save programKey safty:\n" + AES_IV_BASE64 << std::endl;
        AESIV = AES_IV_BASE64;
    }

    auto AES_IV_BIN = utils.Base64_Decode(AESIV);
    std::string AES, IV;
    AES.resize(256/8);
    IV.resize(128/8);

    memcpy(AES.data(), AES_IV_BIN.first.get(), 256/8);
    memcpy(IV.data(), AES_IV_BIN.first.get() + 256/8, 128/8);

    config.setRuntimeValue("LocalAESKey", AES);
    config.setRuntimeValue("LocalAESIV", IV);

    ///
    /// read config file

    if (!readLocalConfigure())
        throw std::runtime_error("read config file failed, please check if programKey is correct");

    // init file manager
    int mode = std::stoi(config.globalValue("TransmissionMode"));
    if (mode == 1) {
        FileManager::getInstance().setAutoWriteEncrypted(false);
    } else {
        FileManager::getInstance().setAutoWriteEncrypted(true);
    }
}

// 保存全局设置
void BusinessInit::saveGlobalConfig() {
    std::ofstream ofs("./CONFIG");
    if (!ofs) {
        DEBUG_INFO("global config file save failed!");
        return ;
    }

    AppConfig& config = AppConfig::getInstance();
    std::string AESKey = config.runtimeValue("LocalAESKey");
    std::string IV = config.runtimeValue("LocalAESIV");

    nlohmann::json globalJson = config.getGlobalStore();
    std::string globalJsonStr = globalJson.dump();

    CipherUtils& utils = CipherUtils::getInstance();

    auto globalJsonAES = utils.AES_Encrypt(globalJsonStr.c_str(),
                                           globalJsonStr.size(),
                                           (unsigned char*)AESKey.c_str(),
                                           (unsigned char*)IV.c_str());

    auto globalJsonBase64 = utils.Base64_Encode(globalJsonAES.first.get(), globalJsonAES.second);

    ofs<<globalJsonBase64;
    ofs.close();
}

bool BusinessInit::readLocalConfigure() {
    std::ifstream ifs;
    CipherUtils& utils = CipherUtils::getInstance();
    AppConfig& config = AppConfig::getInstance();
    // 读全局配置文件
    ifs.open("./CONFIG", std::ios::in);
    if (!ifs) {
        throw std::runtime_error("打开全局配置文件失败: ./CONFIG");
        return false;
    }
    std::string configFileBase64;
    ifs >> configFileBase64;
    ifs.close();
    // 如果当前的文件是空的，则说明是刚创立
    if (configFileBase64.empty()) {
        InitConfigFile();
    } else {
        // 使用密钥去解密文件
        std::string AESKey = config.runtimeValue("LocalAESKey");
        std::string IV = config.runtimeValue("LocalAESIV");

        auto configFileBin = utils.Base64_Decode(configFileBase64);
        auto configFileStr = utils.AES_Decrypt(configFileBin.first.get(),
                                                   configFileBin.second,
                                                   (unsigned char*)AESKey.data(),
                                                   (unsigned char*)IV.data());
        std::string configJson = (char *)configFileStr.first.get();

        if (!isvalidJson(configJson)) {
            DEBUG_INFO("invalid json");
            return false;
        }
        config.init(configJson);


    }
    return true;
}


// judge a string whether a json
bool BusinessInit::isvalidJson(const std::string& str) {
    try {
        auto json = nlohmann::json::parse(str);
        return true; // Parsing successful
    } catch (nlohmann::json::parse_error& e) {
        // Output the error just for demonstration purposes
        DEBUG_INFO((std::string)"Parsing error: " + e.what());
        return false; // Parsing failed, not a valid JSON
    }
}
void BusinessInit::InitConfigFile() {

    std::string jsonStr = R"({"MechineID":"1",
                            "ECC_public":"",
                            "ECC_private":"",
                            "SQLHostName":"0",
                            "SQLPort":"0",
                            "SQLDatabaseName":"0",
                            "SQLUserName":"0",
                            "SQLUserPwd":"0"
                            })";

    nlohmann::json json = nlohmann::json::parse(jsonStr);

    CipherUtils& utils = CipherUtils::getInstance();

    auto eccs = utils.ECC_generate();
    std::string ECC_public(std::move(eccs.first));
    std::string ECC_private(std::move(eccs.second));

    json["ECC_public"] = ECC_public;
    json["ECC_private"] = ECC_private;

    std::string id;
    std::cout << "please input the mechine id[1-255]:(default=1)";
    getline(std::cin, id);
    if (id.empty()) id = "1";
    // if input number > 255, converter to lower 255
    json["MechineID"] = std::to_string((uint8_t)std::stoi(id));
    std::cout << "please input the tranmission mode (default = 1):";
    std::string mode;
    getline(std::cin, mode);
    if (mode.empty()) mode = "1";
    // if input invalid number
    if (std::stoi(mode) > 2 || std::stoi(mode) < 1) {
        mode = "1";
    }
    json["TransmissionMode"] = mode;

    std::cout << "please input database host&port(default=127.0.0.1:3306):";
    std::string host;
    getline(std::cin, host);
    if (host.empty()) {
        host = "127.0.0.1:3306";
    }
    auto k = host.find(':');
    std::string port = host.substr(k + 1);
    host = host.substr(0, k);
    json["SQLHostName"] = host;
    json["SQLPort"] = port;

    std::cout << "please input database user name:";
    std::string databaseUserName;
    getline(std::cin, databaseUserName);
    json["SQLUserName"] = databaseUserName;

    std::cout << "please input database user password:";
    std::string databaseUserPwd;
    getline(std::cin, databaseUserPwd);
    json["SQLUserPwd"] = databaseUserPwd;



    AppConfig::getInstance().init(json.dump());
    DEBUG_INFO(json.dump());
    saveGlobalConfig();
}
